/*
 * Desc： ------
 * @Author jiaxin
 * @Company 亚信科技
 * @Since 2020/7/4 15:42
 *
 */
package com.ybzl.common.crypto;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Security;

public class AESUtil {

    // 16位加密key ，可以自己指定。 也可以通过KeyGenerator生成
    private static final String AES_KEY = "hanhancompleteme";

    private static final String AES_IV = "1993122319920119";

    private static final String KEY_TYPE = "AES";

    // 提供商
    private static final String AES_PROVIDER = "BC";

    // "算法/模式/补码方式"
    private static final String CIPHER_CBC_PKCS5 = "AES/CBC/PKCS5Padding";

    private static final String CIPHER_CBC_PKCS7 = "AES/CBC/PKCS7Padding";

    private static final String CIPHER_ECB_PKCS5 = "AES/ECB/PKCS5Padding";

    /**
     * jdk CBC 模式下加密数据 , 不支持： PKCS7Padding
     * 当分组块相同时，由于向量的存在确保加密后的密文不会重复
     *
     * @param data
     * @return
     */
    public static String jdkAesCBCEncode(String data) {
        try {
            // 转换key
            Key key = new SecretKeySpec(AES_KEY.getBytes(), KEY_TYPE);
            // 算法/模式/填充
            Cipher cipher = Cipher.getInstance(CIPHER_CBC_PKCS5);

            // 初始化向量，确保 相同分组快加密数据不重复
            AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(KEY_TYPE);
            algorithmParameters.init(new IvParameterSpec(AES_IV.getBytes()));

            cipher.init(Cipher.ENCRYPT_MODE, key, algorithmParameters);
            // 对原数据进行算fan运算
            byte[] targetBytes = cipher.doFinal(data.getBytes());
            // 转换成易读模式(jdk提供的base64对字节数据进行转化)
            return Base64.toBase64String(targetBytes);
            // 也可以通过 Hex(16进制)对字节数据进行编码

        } catch (Exception e) {

            e.printStackTrace();
            return null;
        }

    }

    /**
     * jdk CBC 模式下解密数据
     *
     * @param data
     * @return
     */

    public static String jdkAesCBCDecode(String data) {
        try {
            // 转换key
            Key key = new SecretKeySpec(AES_KEY.getBytes(), KEY_TYPE);
            // 算法/模式/填充
            Cipher cipher = Cipher.getInstance(CIPHER_CBC_PKCS5);

            AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(KEY_TYPE);
            algorithmParameters.init(new IvParameterSpec(AES_IV.getBytes()));

            cipher.init(Cipher.DECRYPT_MODE, key, algorithmParameters);

            // 对数据进行处理：
            // 因为加密的时候，对字节数据进行了base64的编码 ，那么执行算法之前，应该对数据进行base64的解码
            byte[] bytes = Base64.decode(data);

            // 对原数据进行算法运算
            byte[] targetBytes = cipher.doFinal(bytes);

            // 此时的数据已经是原始数据的字符数组表现形式，直接返回
            return new String(targetBytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * BC 下 CBC加密数据 , 支持： PKCS7Padding
     * 当分组块相同时，由于向量的存在确保加密后的密文不会重复
     *
     * @param data
     * @return
     */
    public static String bcAesCBCEncode(String data) {
        try {
            // 转换key
            Key key = new SecretKeySpec(AES_KEY.getBytes(), KEY_TYPE);
            // 提供 bc 的provider 填充模式： PKCS7Padding
            Security.addProvider(new BouncyCastleProvider());
            // 算法/模式/填充
            Cipher cipher = Cipher.getInstance(CIPHER_CBC_PKCS7, AES_PROVIDER);

            // 初始化向量，确保 相同分组快加密数据不重复
            AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(KEY_TYPE);
            algorithmParameters.init(new IvParameterSpec(AES_IV.getBytes()));

            cipher.init(Cipher.ENCRYPT_MODE, key, algorithmParameters);
            // 对原数据进行算fan运算
            byte[] targetBytes = cipher.doFinal(data.getBytes());
            // 转换成易读模式(jdk提供的base64对字节数据进行转化)
            return Base64.toBase64String(targetBytes);
            // 也可以通过 Hex(16进制)对字节数据进行编码

        } catch (Exception e) {

            e.printStackTrace();
            return null;
        }

    }

    /**
     * BC下 CBC解密数据
     *
     * @param data
     * @return
     */

    public static String bcAesCBCDecode(String data) {
        try {
            // 转换key
            Key key = new SecretKeySpec(AES_KEY.getBytes(), KEY_TYPE);
            // 提供 bc 的provider , 填充模式： PKCS7Padding
            Security.addProvider(new BouncyCastleProvider());
            // 算法/模式/填充
            Cipher cipher = Cipher.getInstance(CIPHER_CBC_PKCS7, AES_PROVIDER);

            AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(KEY_TYPE);
            algorithmParameters.init(new IvParameterSpec(AES_IV.getBytes()));

            cipher.init(Cipher.DECRYPT_MODE, key, algorithmParameters);

            // 对数据进行处理：
            // 因为加密的时候，对字节数据进行了base64的编码 ，那么执行算法之前，应该对数据进行base64的解码
            byte[] bytes = Base64.decode(data);
            // 对原数据进行算法运算
            byte[] targetBytes = cipher.doFinal(bytes);

            // 此时的数据已经是原始数据的字符数组表现形式，直接返回
            return new String(targetBytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /***
     *  jdk 下： ecb模式下加密数据
     *  会出现：当明文块相同时。加密后的数据相同
     * @param data
     * @return
     */
    public static String jdkAesEncode(String data) {
        try {
            // 转换key
            Key key = new SecretKeySpec(AES_KEY.getBytes(), KEY_TYPE);
            // 算法/模式/填充
            Cipher cipher = Cipher.getInstance(CIPHER_ECB_PKCS5);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            // 对原数据进行算fan运算
            byte[] targetBytes = cipher.doFinal(data.getBytes());

            for (byte temp : targetBytes) {
                // -118 -41 -17 12 42 -22 -76 -124 85 -47 114 100 9 73 10 28
                //System.out.print(temp + " ");
            }
            // 转换成易读模式(jdk提供的base64对字节数据进行转化)
            return Base64.toBase64String(targetBytes);
            // 也可以通过 Hex(16进制)对字节数据进行编码
        } catch (Exception e) {

            e.printStackTrace();
            return null;
        }

    }


    /***
     *  jdk ecb 数据解密
     *  @param data
     *  @return
     */

    public static String jdkAesDecode(String data) {
        try {
            // 转换key
            Key key = new SecretKeySpec(AES_KEY.getBytes(), KEY_TYPE);
            // 算法/模式/填充
            Cipher cipher = Cipher.getInstance(CIPHER_ECB_PKCS5);
            cipher.init(Cipher.DECRYPT_MODE, key);

            // 对数据进行处理：
            // 因为加密的时候，对字节数据进行了base64的编码 ，
            // 那么执行算法之前，应该对数据进行base64的解码
            byte[] bytes = Base64.decode(data);

            // 对原数据进行算法运算
            byte[] targetBytes = cipher.doFinal(bytes);

            // 此时的数据已经是原始数据的字符数组表现形式，直接返回
            return new String(targetBytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 采用BC的Base64编码
     *
     * @param data data
     * @return String String
     */
    public static String aecEnc(String data) {
        try {
            Key key = new SecretKeySpec(AES_KEY.getBytes(), KEY_TYPE);
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance(CIPHER_CBC_PKCS7, AES_PROVIDER);
            AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(KEY_TYPE);
            algorithmParameters.init(new IvParameterSpec(AES_IV.getBytes()));
            cipher.init(Cipher.ENCRYPT_MODE, key, algorithmParameters);
            byte[] bytes = cipher.doFinal(data.getBytes());
            return Base64.toBase64String(bytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 采用BC的Base64编码
     *
     * @param data data
     * @return String String
     */
    public static String aecDec(String data) {
        try {
            Key key = new SecretKeySpec(AES_KEY.getBytes(), KEY_TYPE);
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance(CIPHER_CBC_PKCS7, AES_PROVIDER);
            AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(KEY_TYPE);
            algorithmParameters.init(new IvParameterSpec(AES_IV.getBytes()));
            cipher.init(Cipher.DECRYPT_MODE, key, algorithmParameters);
            byte[] bytes = Base64.decode(data);
            byte[] targetBytes = cipher.doFinal(bytes);
            return new String(targetBytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
